var fs = require('fs');
var path = require('path');
var stream = require('stream');
var _ = require('lodash');
var chalk = require('chalk');
var del = require('del');
var exec = require('child_process').exec;
var shelljs = require('shelljs');
var glob = require('glob');
var ejs = require('ejs');

ejs.delimiter = '?';




// import fs from "fs";
// import _ from "lodash";

class Build {

    constructor() {
        //declare
        this.taskMap = {};
        this.config = {};
        this.params = {};
        this.msg = '';
        this.root = '';

        //init
        this.initRoot()
            .initConfig()
            .initTaskMap();
    }



    //tasks
    buildDev() {
        var cmdStr = 'node build -a rebuild';
        exec(cmdStr, (err, stdout, stderr) => {

            if (err) {
                console.log('get weather api error:' + stderr);
            } else {
                /*
                这个stdout的内容就是上面我curl出来的这个东西：
                {"weatherinfo":{"city":"北京","cityid":"101010100","temp":"3","WD":"西北风","WS":"3级","SD":"23%","WSE":"3","time":"21:20","isRadar":"1","Radar":"JC_RADAR_AZ9010_JB","njd":"暂无实况","qy":"1019"}}
                */
                var data = stdout;
                //console.log(data);
            }

        });
    }

    addTest() {
        let dirs = glob.sync(this.root + this.config.singleComponentDir + '/*/*');
        let testDirs = dirs.map(v => v + '/__test__');
        testDirs.forEach(tdir => {
            if (!fs.existsSync(tdir)) {
                shelljs.mkdir('-p', tdir);
                this.write(null, null, tdir + '/index.js');
                this.write(null, null, tdir + '/index.less');
            }
        });

    }

    rebuildSingleDebugView() {
        let components = glob.sync(this.root + this.config.singleComponentDir + '/*/*');
        let json = JSON.stringify(components.map(v => {
            let arr = v.split('/');
            return {
                name: arr.pop(),
                type: arr.pop(),
            };
        }));
        //console.log(this.root + this.config.singleDebugViewTplDir);
        let strTpl = fs.readFileSync(this.root + this.config.singleDebugViewTplDir, 'utf8');
        let strView = ejs.render(strTpl, {
            items: json
        });
        this.write(null, strView, this.root + this.config.singleDebugViewDir)
            .setMsg(chalk.green('ok => single dubug view success build: ') + chalk.yellow.underline(this.root + this.config.singleDebugViewDir))
            .log();

    }

    rebuildSingleDebugRouter() {
        var comps = _.chain(this.getAllComponentsDirWith())
            .map((compDir) => {
                return {
                    component: compDir.split('/').join(''),
                    importDir: this.compareDir(this.config.singleComponentDir + '/' + compDir, this.config.singleDebugRouterDir) + '/__test__',
                    path: compDir,
                };
            })
            .map((compObj) => {
                return [
                    `import ${compObj.component} from "${compObj.importDir}";`,
                    `{ component:${compObj.component}, path:"${compObj.path}" }`
                ];
            })
            .unzip()
            .map((strItems, key) => {

                return strItems.join(!key ? '\n' : ',\n    ');
            })
            .value();
        var strComps = comps.join('\n\nexport default [\n    ') + '\n];';
        var routerDir = this.root + this.config.singleDebugRouterDir;

        this.write(null, strComps, routerDir)
            .setMsg(chalk.green('ok => single dubug router success build: ') + chalk.yellow.underline(routerDir))
            .log();

        return this;
    }

    rebuildAppLess() {
        /*var appLessDir = this.root + this.config.appLessDir;
        var singlePathRelativeToAppLess = this.compareDir(this.root + this.config.singleComponentDir, appLessDir);

        var allComponentsDir = this.getAllComponentsDirWith(
            (compDir) => ('@import "' + singlePathRelativeToAppLess + '/' + compDir + '/index.less";'),
            (compDir) => ('@import "' + singlePathRelativeToAppLess + '/' + compDir + '/index.less";')
        );

        var writable = fs.createWriteStream(appLessDir);
        var readable = new stream.Readable;
        readable._read = () => {};
        readable.push(allComponentsDir.join('\n'));
        readable.pipe(writable);
        this.setMsg(chalk.green('ok => update app.less success (cmd+click): ') + chalk.yellow.underline(appLessDir)).log();*/

        return this;
    }

    buildProject() {
        let buildDirs = [
            this.config.projectDir,
            this.config.coreDir,
            this.config.libsDir,
            this.config.etcDir,
            this.config.langDir,
            this.config.multiComponentDir,
            this.config.singleComponentDir,
            this.config.singleServiceComponentDir,
            this.config.singleWidgetComponentDir,
            this.config.singleLayoutComponentDir,
            this.config.multiLogicComponentDir,
            this.config.multiEntryComponentDir,
            this.config.multiWidgetComponentDir,
            this.config.multiLayoutComponentDir,
        ];
        //this.config.singleDebugRouterDir,
        buildDirs.forEach((dir) => {
            if (!fs.existsSync(this.root + dir)) {
                shelljs.mkdir('-p', this.root + dir);
            } else {
                throw new Error(chalk.red('fail => this project have alread init (cmd+click): ') + chalk.yellow.underline(this.root + dir));
            }

        });
        let files = glob.sync(this.root + this.config.coreTplDir + '/*.*');
        files.forEach((tplFile) => {
            let targetfile = this.root + this.config.coreDir + '/' + tplFile.split('/').pop();
            this.write(tplFile, null, targetfile);
        });

    }

    createComponent(type, name) {

        let { dir, tpldir } = this.getDirWhen(type);

        let root = this.root;
        let newdir = root + dir + `/${name}`;
        tpldir = root + tpldir;

        if (!fs.existsSync(newdir)) {
            shelljs.mkdir('-p', newdir + '/__test__');
            //fs.mkdirSync(newdir, '0777');
        } else {
            throw new Error(chalk.red('fail => this component have alread exsited (cmd+click): ') + chalk.yellow.underline(newdir));
        }

        let files = glob.sync(tpldir + '/*.*');

        files.map((fileDir) => {
            let filename = fileDir.split('/').pop();

            let tplfile = tpldir + '/' + filename;
            let newfile = newdir + '/' + filename;
            let tplstr = fs.readFileSync(tplfile, 'utf8');


            let data = {};
            let fArr = filename.split('.');
            if (type == 'ss') {
                if (~fArr.indexOf('less')) {
                    var relativeDir = this.compareDir(this.root + this.config.coreLessDir, newfile);
                    data = {
                        coreLessDir: relativeDir,
                        className: 'orin-' + type + '-' + name.toLowerCase() + '-root'
                    };

                } else if ((~fArr.indexOf('js')) && (~fArr.indexOf('view'))) {
                    data = {
                        componentName: name,
                        className: 'orin-' + type + '-' + name.toLowerCase() + '-root'
                    };

                } else if (~fArr.indexOf('js') && ~fArr.indexOf('store')) {

                    data = {
                        componentName: name
                    };
                }
            }


            if (type == 'sl' || type == 'sw') {
                if (~fArr.indexOf('less')) {
                    var relativeDir = this.compareDir(this.root + this.config.coreLessDir, newfile);
                    data = {
                        coreLessDir: relativeDir,
                        className: 'orin-' + type + '-' + name.toLowerCase() + '-root'
                    };

                } else if ((~fArr.indexOf('js')) && (~fArr.indexOf('view'))) {
                    data = {
                        componentName: name,
                        className: 'orin-' + type + '-' + name.toLowerCase() + '-root'
                    };

                }
            }



            if (type == 'me') {
                if (~fArr.indexOf('js') && ~fArr.indexOf('Router')) {
                    data = {
                        componentName: name
                    };

                } else if (~fArr.indexOf('less')) {
                    var relativeDir = this.compareDir(this.root + this.config.coreLessDir, newfile);
                    data = {
                        coreLessDir: relativeDir,
                        className: 'orin-' + type + '-' + name.toLowerCase() + '-root'
                    };

                } else if (~fArr.indexOf('tpl') && ~fArr.indexOf('view')) {
                    data = {
                        componentName: name,
                        className: 'orin-' + type + '-' + name.toLowerCase() + '-root'
                    };

                } else if (~fArr.indexOf('js') && ~fArr.indexOf('App')) {
                    data = {
                        componentName: name
                    };

                } else if (~fArr.indexOf('js') && ~fArr.indexOf('main')) {
                    data = {
                        componentName: name
                    };

                }

            }


            if (type == 'mlg' || type == 'mly') {
                if (~fArr.indexOf('js') && ~fArr.indexOf('main')) {
                    data = {
                        componentName: name
                    };

                } else if (~fArr.indexOf('less')) {
                    var relativeDir = this.compareDir(this.root + this.config.coreLessDir, newfile);
                    data = {
                        coreLessDir: relativeDir,
                        className: 'orin-' + type + '-' + name.toLowerCase() + '-root'
                    };

                } else if (~fArr.indexOf('tpl') && ~fArr.indexOf('view')) {
                    data = {
                        componentName: name,
                        className: 'orin-' + type + '-' + name.toLowerCase() + '-root'
                    };

                }
            }


            let targetStr = ejs.render(tplstr, {
                orin: data
            });
            let testData = _.mapValues(data, function(value, key, list) {
                //console.log(key);
                if (key == 'className') return `${value}-test`;
                if (key == 'coreLessDir') return '../' + value;
                if (key == 'componentName') return value;


                return value;

            });
            //console.log(testData);

            if (~['ss', 'sl', 'sw'].indexOf(type)) {

                let tplTestFile = tpldir + '/__test__/' + filename;
                let newTestFile = newdir + '/__test__/' + filename;
                let tplTestStr = fs.readFileSync(tplTestFile, 'utf8');

                let targetTestStr = ejs.render(tplTestStr, {
                    orin: testData
                });
                if (type != 'ss' && filename == 'view.js') {
                    //console.log(filename);
                    ['actions', 'store'].forEach((filename) => {
                        this.write(null, ejs.render(fs.readFileSync(`${tpldir}/__test__/${filename}.js`, 'utf8'), {
                            orin: testData
                        }), `${newdir}/__test__/${filename}.js`);
                    });

                } else {


                }

                this.write(null, targetTestStr, newTestFile);

            }

            this.write(null, targetStr, newfile);

        });

        this.setMsg(chalk.green('ok => success create component (cmd+click): ') + chalk.yellow.underline(newdir)).log()
            .rebuildAppLess()
            .rebuildSingleDebugRouter();

        return this;
    }


    updateComponent(prop, data) {
        if (prop == 'name') {
            console.log(data);
            var { type, oldName, newName } = data;
            var { dir: fulldir } = this.getDirWhen(type);

            var dir = fulldir.substr(5);
            var componentFilename = `${dir}/${oldName}`;
            var componentNewFilename = `${dir}/${newName}`;

            //rename
            var componentBundle = this.root + fulldir + `/${oldName}`;
            var newComponentBundle = this.root + fulldir + `/${newName}`;
            try {

                fs.renameSync(componentBundle, newComponentBundle);
                let msg = chalk.green('success => component have been rename (cmd+click): ') + chalk.yellow.underline(newComponentBundle);
                this.setMsg(msg).log();


            } catch (e) {
                let msg = chalk.red('fail => component have not exsit (cmd+click): ') + chalk.yellow.underline(componentBundle);
                this.setMsg(msg).log();

                throw e;
            }


            glob(this.root + this.config.singleComponentDir + '/*/*/view.js', null, (er, files) => {
                files.forEach(file => {
                    let fileData = fs.readFileSync(file, 'utf8');

                    if (~fileData.indexOf(componentFilename)) {

                        fs.writeFileSync(file, fileData.replace(componentFilename, componentNewFilename), 'utf8');
                        let msg = chalk.green('success => target component s path have been rename in  (cmd+click): ') + chalk.yellow.underline(file);
                        this.setMsg(msg).log();
                    }
                });

                this.rebuildSingleDebugRouter();
            });
        }
    }

    deleteComponent(type, name) {
        let { dir, tpldir } = this.getDirWhen(type);

        var root = path.dirname(path.dirname(path.dirname(__filename)));

        var componentDir = root + dir + `/${name}`;
        var msg = '';
        if (fs.existsSync(componentDir)) {

            del(componentDir, { force: true });

            msg = chalk.green('ok => success delete the component (cmd+click): ') + chalk.yellow.underline(componentDir);
        } else {
            throw new Error(chalk.red('fail => no component about to delete (cmd+click): ') + chalk.yellow.underline(componentDir));
        }
        this.setMsg(msg).log()
            .rebuildAppLess()
            .rebuildSingleDebugRouter();

        return this;


    }

    //util

    getDirWhen(type) {
        var dir, tpldir;
        if (type == 'sw') {
            dir = this.config.singleWidgetComponentDir;
            tpldir = this.config.singleWidgetTplDir;

        } else if (type == 'ss') {
            dir = this.config.singleServiceComponentDir;
            tpldir = this.config.singleServiceTplDir;
        } else if (type == 'sl') {
            dir = this.config.singleLayoutComponentDir;
            tpldir = this.config.singleLayoutTplDir;
        } else if (type == 'me') {
            dir = this.config.multiEntryComponentDir;
            tpldir = this.config.multiEntryTplDir;
        } else if (type == 'mw') {
            dir = this.config.multiWidgetComponentDir;
            tpldir = this.config.multiWidgetTplDir;
        } else if (type == 'mlg') {
            dir = this.config.multiLogicComponentDir;
            tpldir = this.config.multiLogicTplDir;
        } else if (type == 'mly') {
            dir = this.config.multiLayoutComponentDir;
            tpldir = this.config.multiLayoutTplDir;
        } else {
            dir = this.config.singleServiceComponentDir;
            tpldir = this.config.singleServiceTplDir;
        }

        return { dir, tpldir };
    }

    getAllComponentsDirWith(shareCallback, businessCallback) {
        shareCallback = shareCallback || ((compDir) => (compDir));
        businessCallback = businessCallback || ((compDir) => (compDir));
        var dirArr = [
            this.config.singleWidgetComponentDir,
            this.config.singleServiceComponentDir,
            this.config.singleLayoutComponentDir
        ];

        let dirs = dirArr.map(dir => this.root + dir);
        let lasts = dirArr.map(dir => dir.split('/').pop());

        let _allComponentsDir = [];
        for (var i = 0; i < dirs.length; i++) {
            _allComponentsDir = _.chain(_allComponentsDir).union(
                _.chain(fs.readdirSync(dirs[i]))
                .filter((compName) => {

                    return !/^\..+/g.test(compName);

                })
                .map((compName) => {

                    return businessCallback(lasts[i] + '/' + compName);

                }).value()
            );
        }
        let allComponentsDir = _allComponentsDir.value();


        /*var allComponentsDir = _.chain(fs.readdirSync(businDir))
            .filter((compName) => {

                return !/^\..+/g.test(compName);

            })
            .map((compName) => {

                return businessCallback(businLast + '/' + compName);

            })
            .union(
                _.chain(fs.readdirSync(shareDir))
                .filter((compName) => {
                    return !/^\..+/g.test(compName);
                })
                .map((compName) => {
                    return shareCallback(shareLast + '/' + compName);
                })
                .value()
            ).value();*/
        return allComponentsDir;
    }

    write(fromFilePath, withData, toFilePath) {

        var readable = new stream.Readable;
        var writable = fs.createWriteStream(toFilePath);

        if (fromFilePath) {
            readable = fs.createReadStream(fromFilePath);
        } else {
            readable._read = () => {};
        }

        if (withData) {

            readable.push(withData);
        }

        readable.pipe(writable);

        return this;
    }
    compareDir(beComparedFile, file) {

        var fArr = file.split('/');
        var beArr = beComparedFile.split('/');
        fArr.shift();
        beArr.shift();

        while (fArr && beArr && fArr[0] == beArr[0]) {
            fArr.shift();
            beArr.shift();
        }

        if (fArr.length > 1) {
            return new Array(fArr.length).join('../') + beArr.join('/');
        } else {
            return false;
        }

    }

    log() {

        console.log(this.msg);
        return this;
    }

    del(path) {

        var files = [];

        if (fs.existsSync(path)) {

            files = fs.readdirSync(path);

            files.forEach((file, index) => {

                var curPath = path + '/' + file;

                if (fs.statSync(curPath).isDirectory()) { // recurse

                    this.del(curPath);

                } else { // delete file

                    fs.unlinkSync(curPath);

                }

            });

            fs.rmdirSync(path);

        }

    }

    // run;
    switchTask() {
        var p = this.params;

        if (p['action']) {
            if (p['action'] == 'rebuild' && p['type'] == 'less') {

                return this.rebuildAppLess();

            } else if (p['action'] == 'rebuild' && p['type'] == 'sdr') {

                return this.rebuildSingleDebugRouter();

            } else if (p['action'] == 'rebuild') {

                return this.rebuildAppLess()
                    .rebuildSingleDebugRouter()
                    .rebuildSingleDebugView()

            } else if (p['action'] == 'dev') {

                return this.buildDev();

            } else if (p['action'] == 'fill_test') {
                return this.addTest();
            }
        } else if (p['delete']) {

            p['type'] = p.type ? p.type : 'ss';
            return this.deleteComponent(p.type, p.delete);


        } else if (p['create']) {

            p['type'] = p.type ? p.type : 'ss';
            return this.createComponent(p.type, p.create);


        } else if (p['update']) {
            if (p['name'] && p['type']) {
                var { prop, oldName, newName, type } = {
                    type: p.type,
                    prop: 'name',
                    oldName: p.update,
                    newName: p.name
                }
                return this.updateComponent(prop, { oldName, newName, type });
            } else {
                throw new Error(chalk.red('fail => what you want to update?'));
            }
        } else if (p['init']) {
            return this.buildProject();

        }

        throw new Error(chalk.red('fail => no task to run'));
    }

    run(params) {

        this.setParams(params)
            .transformParamsToClear()
            .switchTask();

        return this;

    }
    transformParamsToClear() {
        return this;
    }

    setParams(params) {
        this.params = params;
        return this;
    }

    setMsg(msg) {
        this.msg = msg;
        return this;
    }



    // init;
    initRoot() {
        this.root = path.dirname(path.dirname(path.dirname(__filename)));
        return this;
    }
    initConfig() {

        var config = fs.readFileSync('./etc/config.json', 'utf8');
        config = eval('(' + config + ')');

        this.config = _.extend({
            singleWidgetComponentDir: '',
            singleBusinessComponentDir: ''
        }, config);
        return this;
    }

    initTaskMap() {
        var json = fs.readFileSync('./etc/params.json', 'utf8');
        this.taskMap = eval('(' + json + ')');

        _.chain(this.taskMap).values().uniq().each((val, key) => {
            this.taskMap['--' + val] = val;
        });

        return this;
    }



}

module.exports = Build;